1 00:00:00,660 --> 00:00:01,560 Welcome back. 2 00:00:01,560 --> 00:00:06,390 In this lecture we're going to continue where we left off by scripting our leaderboard to save a player's 3 00:00:06,390 --> 00:00:08,100 data in a data store. 4 00:00:08,100 --> 00:00:11,070 Specifically, we want to save the player's kills and deaths. 5 00:00:11,070 --> 00:00:16,980 So when they rejoin the game in the future, we can get their data so we can go ahead and get the data 6 00:00:16,980 --> 00:00:18,150 store service. 7 00:00:23,060 --> 00:00:26,240 And then we can go ahead and create a data store for our players. 8 00:00:26,240 --> 00:00:31,430 If you would like to have the players kills and deaths be ordered, then you'll have to create two ordered 9 00:00:31,430 --> 00:00:34,880 data stores, one for player deaths and another for player kills. 10 00:00:34,880 --> 00:00:39,500 However, I am not concerned about ordering the data, so I'm just going to store the player's data 11 00:00:39,500 --> 00:00:43,670 in one data store and I'm going to call it my player leaderboard. 12 00:00:44,840 --> 00:00:46,310 Data store. 13 00:00:46,310 --> 00:00:48,560 And that's going to be equal to data store service. 14 00:00:48,560 --> 00:00:53,420 And we'll get a new data store and we'll call it player stats. 15 00:00:53,990 --> 00:00:58,700 And as we've seen in a previous lecture, we're also going to want to keep track of any players that 16 00:00:58,700 --> 00:01:01,580 may have errored when we try to grab their data. 17 00:01:01,580 --> 00:01:08,150 So we'll create a table called Errored players, and we'll store them in there to prevent us from saving 18 00:01:08,150 --> 00:01:13,670 any data and overwriting their previous data so we can, you know, prevent data loss. 19 00:01:14,120 --> 00:01:16,310 So now we can go ahead and create a few functions. 20 00:01:16,310 --> 00:01:18,740 One we can have for saving a player's data. 21 00:01:18,740 --> 00:01:19,970 We'll call it save data for. 22 00:01:19,970 --> 00:01:21,380 And we'll pass a player. 23 00:01:21,380 --> 00:01:24,980 We'll also want to have a function for grabbing the data for a player. 24 00:01:24,980 --> 00:01:29,360 So we could call this get data for and then pass a player. 25 00:01:29,450 --> 00:01:35,240 And then we could also have a function for saving the data of every single player in our game. 26 00:01:35,240 --> 00:01:38,660 So that would be save data for all players. 27 00:01:38,720 --> 00:01:44,270 And this one would just simply loop through all of the players in our game and just call this function 28 00:01:44,270 --> 00:01:44,690 on them. 29 00:01:44,690 --> 00:01:49,790 So we would loop through every single player in players get players. 30 00:01:52,180 --> 00:01:57,340 And what we want to do is we want to go ahead and save the data for this player. 31 00:01:57,340 --> 00:02:03,880 But of course, since data stores are going to yield, we'll want to spawn this in a separate thread. 32 00:02:04,820 --> 00:02:09,230 So that way we're not yielding in this for loop. 33 00:02:10,600 --> 00:02:15,010 The next thing we could go ahead and do is we could go ahead and listen to when a player leaves our 34 00:02:15,010 --> 00:02:15,340 game. 35 00:02:15,340 --> 00:02:19,030 So player removing, we'll connect and get our player. 36 00:02:20,840 --> 00:02:26,990 And what we can go ahead and do is just basically call the save data for function and pass our player. 37 00:02:27,230 --> 00:02:30,080 So let's go ahead and actually fill this function out. 38 00:02:30,080 --> 00:02:33,980 What data do we want to save for a particular player. 39 00:02:34,250 --> 00:02:37,550 Well we want to go ahead and save their kills and deaths. 40 00:02:37,550 --> 00:02:42,890 So that means we're going to use the player leaderboard data store and use the update async function 41 00:02:42,890 --> 00:02:43,730 on this player. 42 00:02:43,730 --> 00:02:47,990 And we're going to use their user ID as the key. 43 00:02:48,350 --> 00:02:51,830 And of course we need to pass a function to this data store. 44 00:02:51,830 --> 00:02:55,280 And it's going to give us the old data at this key. 45 00:02:55,940 --> 00:03:00,710 And because we're using an asynchronous function, you need to remember like we did last time, we have 46 00:03:00,710 --> 00:03:02,150 to wrap this in a p call. 47 00:03:02,150 --> 00:03:03,770 So we'll have success. 48 00:03:03,770 --> 00:03:06,260 And then error that's equal to p call. 49 00:03:07,450 --> 00:03:10,330 And then we can just take this and paste that in there. 50 00:03:10,480 --> 00:03:16,690 Now, if this is a new player where saving data for, of course they're not going to have any old data. 51 00:03:16,690 --> 00:03:23,710 So if we do not have any old data, then we can go ahead and give them the default template for saving 52 00:03:23,710 --> 00:03:24,040 data. 53 00:03:24,040 --> 00:03:25,420 It'll just be a table. 54 00:03:25,420 --> 00:03:30,310 And inside of here we can have a key one for kills which will be zero by default. 55 00:03:30,310 --> 00:03:34,210 And then we'll have a key for deaths, which is also zero by default. 56 00:03:34,450 --> 00:03:38,470 Then what we can go ahead and do is get the kills and deaths for our player. 57 00:03:38,470 --> 00:03:42,790 So the kills is going to be equal to our players Leaderstats folder. 58 00:03:42,790 --> 00:03:46,960 And we'll get the kills int value and get the value stored in there. 59 00:03:47,470 --> 00:03:50,470 And then we can also go ahead and get the deaths for our player. 60 00:03:50,470 --> 00:03:55,450 And it's equal to player dot Leaderstats dot deaths dot value. 61 00:03:56,050 --> 00:04:02,080 And now inside of the old data table, we can go ahead and update the keys like kills, and we want 62 00:04:02,080 --> 00:04:05,890 to set it equal to the kills stored in our leader stats. 63 00:04:05,920 --> 00:04:13,840 Now let's say just in case, for some reason the kills value here is somehow less than the value stored 64 00:04:13,840 --> 00:04:15,040 in old data. 65 00:04:15,040 --> 00:04:17,230 Well, that means we don't want to overwrite it. 66 00:04:17,230 --> 00:04:21,820 Our kills value shouldn't ever be less than what's already stored in the data store. 67 00:04:21,820 --> 00:04:30,010 So we can put an if else statement here, and we can check if kills is greater than our old data dot 68 00:04:30,010 --> 00:04:30,580 kills. 69 00:04:30,580 --> 00:04:35,440 If it is, then we can go ahead and pass kills as the new value to set in this key. 70 00:04:35,470 --> 00:04:37,240 Otherwise we don't want to overwrite it. 71 00:04:37,240 --> 00:04:42,760 So we'll just pass the same data that was already there, and then we can copy this and do the exact 72 00:04:42,760 --> 00:04:43,780 same thing. 73 00:04:44,580 --> 00:04:46,260 But for our deaths. 74 00:04:46,590 --> 00:04:51,420 So if our deaths is greater than all data kills. 75 00:04:52,380 --> 00:04:54,240 We need to swap that to deaths. 76 00:04:54,570 --> 00:04:57,540 Then we can go ahead and pass the new deaths of the player. 77 00:04:57,540 --> 00:05:01,410 Otherwise we'll pass the old deaths of the player. 78 00:05:01,410 --> 00:05:07,440 So this is an if else block that we can put in as a condition to check something. 79 00:05:07,440 --> 00:05:11,820 And then we can go ahead and return a value based on this condition. 80 00:05:12,420 --> 00:05:17,640 So if the player's current kills are greater than their old kills, then great will update it with that 81 00:05:17,640 --> 00:05:18,450 new kills value. 82 00:05:18,450 --> 00:05:20,550 Otherwise we'll leave the data the same. 83 00:05:20,550 --> 00:05:22,110 And the same goes for the deaths. 84 00:05:22,110 --> 00:05:26,640 If the deaths are greater than what was previously stored, will update it with this new value. 85 00:05:26,640 --> 00:05:28,740 Otherwise we'll keep the old value. 86 00:05:29,070 --> 00:05:33,690 And then last but not least, we need to return this data back to our data store. 87 00:05:34,570 --> 00:05:36,610 Now if we aren't successful. 88 00:05:37,320 --> 00:05:44,430 And saving this data, then we can give out a warning like we failed to save data for player, and then 89 00:05:44,430 --> 00:05:48,360 we could just give the error message for what exactly happened. 90 00:05:49,550 --> 00:05:55,130 And then something else we could do is we could also return the success boolean from this function. 91 00:05:55,130 --> 00:05:58,280 And then we could go ahead and grab that right here. 92 00:05:59,380 --> 00:06:05,740 And what we can do here is if a player joins our game and for some reason we fail to save their data, 93 00:06:05,740 --> 00:06:13,690 then we can go ahead and have like a repeat loop continually executing this function, uh, maybe a 94 00:06:13,690 --> 00:06:21,310 maximum of five times until we either save the player's data or we go beyond a certain threshold. 95 00:06:21,310 --> 00:06:26,950 So if we were not successful with saving this player's data, then I'm going to create a variable. 96 00:06:26,950 --> 00:06:29,470 I'll call it tries, set it equal to zero. 97 00:06:29,470 --> 00:06:36,880 And we're going to continually repeat trying to save the data for this player until we are successful 98 00:06:36,880 --> 00:06:38,440 in saving the player's data. 99 00:06:38,440 --> 00:06:42,340 So we could do success is equal to what is returned from this function. 100 00:06:42,340 --> 00:06:46,750 Or if our tries variable goes beyond the value of like five. 101 00:06:46,750 --> 00:06:51,880 So each time through this loop we want to yield because we don't want to overwhelm our data store. 102 00:06:51,880 --> 00:06:53,500 We'll yield for like 30s. 103 00:06:53,950 --> 00:06:57,640 And then we'll also increment our tries by one each time. 104 00:06:57,640 --> 00:07:03,220 So this will attempt to save the data for our player five times if we fail the first time. 105 00:07:03,220 --> 00:07:06,760 And if we're never successful, then you know, oh well, that sucks. 106 00:07:06,760 --> 00:07:08,080 We lost this player's data. 107 00:07:08,080 --> 00:07:13,150 But you know that isn't too bad because it's just one game session. 108 00:07:13,570 --> 00:07:19,780 But now that we have this setup, we can go ahead and also grab data from our data store to update the 109 00:07:19,780 --> 00:07:22,840 values stored in our int values for the player. 110 00:07:23,080 --> 00:07:27,370 So inside of the get data for function, what we can go ahead and do. 111 00:07:28,170 --> 00:07:34,980 As get the success and result from our P call, and this time we want to refer to our player leaderboard 112 00:07:34,980 --> 00:07:37,590 data store and we want to get async. 113 00:07:37,590 --> 00:07:42,420 We want to get the data for a particular player and we'll return that. 114 00:07:44,290 --> 00:07:49,180 And if we were not successful in grabbing this player's data, then we can go ahead and say something 115 00:07:49,180 --> 00:07:50,860 like error. 116 00:07:51,500 --> 00:07:58,550 And countered when trying to grab player data, and we'll pass the error into the console. 117 00:08:00,080 --> 00:08:04,880 And then we can go ahead and mark this player as errored inside of our Errored players table. 118 00:08:04,880 --> 00:08:07,670 So Errored players will use their name as the key. 119 00:08:07,700 --> 00:08:09,260 Set that equal to true. 120 00:08:09,710 --> 00:08:17,840 And then we'll just set result equal to a default table with kill set to zero and deaths set to zero. 121 00:08:18,410 --> 00:08:21,500 And then we can go ahead and return result at the end of this function. 122 00:08:22,470 --> 00:08:28,710 So now when our player joins the game, we want to go ahead and update the values inside of here to 123 00:08:28,710 --> 00:08:31,710 reflect the data for this player. 124 00:08:32,890 --> 00:08:34,990 So we can get their data. 125 00:08:34,990 --> 00:08:36,970 And that's going to be equal to. 126 00:08:37,660 --> 00:08:40,360 Get data for and we'll pass the player. 127 00:08:41,190 --> 00:08:47,040 And then now that we have this data, when we create the int value of kills, we can go ahead and set 128 00:08:47,040 --> 00:08:50,880 this value equal to our data and get kills. 129 00:08:50,880 --> 00:08:56,070 And then for their deaths we can do the same thing, get their data and their deaths. 130 00:08:56,920 --> 00:09:01,750 Now, another important thing that we make sure we do is that inside of our save data for function, 131 00:09:01,750 --> 00:09:07,600 we want to check if the player were saving data for is not an errored player. 132 00:09:07,840 --> 00:09:13,690 So if this player is inside of our Errored players table, so we'll pass player dot name. 133 00:09:13,690 --> 00:09:19,150 If they're in there, then we need to return and we can give a warning like. 134 00:09:19,940 --> 00:09:23,450 Unable to save data for. 135 00:09:24,780 --> 00:09:26,970 An Erard player. 136 00:09:28,370 --> 00:09:29,960 And then we'll make sure to return. 137 00:09:30,290 --> 00:09:37,460 Another thing we need to make sure of is that this player isn't a fake player or a player from when 138 00:09:37,460 --> 00:09:39,740 we do a local server testing. 139 00:09:40,070 --> 00:09:43,670 So remember, we need to check if this player's user ID. 140 00:09:43,940 --> 00:09:48,800 If it's less than one, then we don't want to save data for them because that will screw up our data 141 00:09:48,800 --> 00:09:49,070 store. 142 00:09:49,070 --> 00:09:52,610 We don't want to have a key that is a negative value. 143 00:09:52,610 --> 00:09:54,740 That's just it's just not going to work. 144 00:09:55,600 --> 00:10:01,900 And then another thing we want to do is when a player does leave our game, we want to make sure if 145 00:10:01,900 --> 00:10:06,100 they were in error player, then we remove them from the table. 146 00:10:06,100 --> 00:10:09,940 So actually the easiest way we could do this is just refer to our players. 147 00:10:10,920 --> 00:10:16,770 And get the key with this player's name and just set it equal to nil, and that virtually just removes 148 00:10:16,770 --> 00:10:18,120 it from our table. 149 00:10:18,450 --> 00:10:24,240 Now another two important things we need to do with our data stores is we want to intermittently save 150 00:10:24,240 --> 00:10:26,580 a player's data maybe every five minutes. 151 00:10:26,580 --> 00:10:32,280 And then we also want to save the data for all the players in our game when, for example, a server 152 00:10:32,280 --> 00:10:33,300 shuts down. 153 00:10:33,390 --> 00:10:38,310 So we're going to use the bind to close function and pass a function here. 154 00:10:38,310 --> 00:10:41,610 And this is where we would call our save data for all players. 155 00:10:41,610 --> 00:10:45,570 So when our game is shutting down we'll make sure to save all of our data. 156 00:10:46,440 --> 00:10:49,980 And then we'll also have a while loop down here. 157 00:10:51,580 --> 00:10:55,390 And what we can do is we can wait like five minutes. 158 00:10:55,390 --> 00:10:56,860 So that will be 300 seconds. 159 00:10:56,860 --> 00:11:00,970 So every five minutes we're saving data for all of the players in our game. 160 00:11:00,970 --> 00:11:05,200 And this is how we set up the data store for our leaderboard. 161 00:11:06,540 --> 00:11:08,730 So let's go ahead and try it out. 162 00:11:08,970 --> 00:11:10,530 If we play our game. 163 00:11:12,660 --> 00:11:17,910 Um, we are getting an error, and that's because we have not enabled access to API services. 164 00:11:17,910 --> 00:11:25,260 So let me actually go to my game settings and go to security and let me enable studio access to API 165 00:11:25,290 --> 00:11:26,130 services. 166 00:11:26,130 --> 00:11:28,110 So we can actually test this out. 167 00:11:28,110 --> 00:11:30,390 And let me try playtesting it again. 168 00:11:31,230 --> 00:11:33,990 And we're still getting this error for some reason. 169 00:11:36,270 --> 00:11:39,840 So maybe it'll take just a second for it to actually update. 170 00:11:44,970 --> 00:11:48,990 Let me make sure to just save it to Roblox just in case. 171 00:11:48,990 --> 00:11:52,650 Or maybe I'll publish it to Roblox too, just in case. 172 00:11:53,630 --> 00:11:56,540 And now let me see if we can go ahead and test it out. 173 00:11:59,530 --> 00:12:01,030 And we did get an error here. 174 00:12:01,030 --> 00:12:04,480 Actually attempt to index nil with kills. 175 00:12:04,480 --> 00:12:07,120 So in our get data for function. 176 00:12:07,940 --> 00:12:12,110 We are assuming that there's actually going to be a result here. 177 00:12:12,110 --> 00:12:18,440 So when a player first joins our game, if we successfully grab data from the data store and this is 178 00:12:18,440 --> 00:12:23,060 their first time joining the game, that means result is going to be nil. 179 00:12:23,180 --> 00:12:30,200 So if result is nil, then we need to make sure to set it to this default value right here. 180 00:12:30,200 --> 00:12:33,230 So what I'm going to do is I'm actually going to grab this. 181 00:12:34,380 --> 00:12:36,150 And then we could have another if statement. 182 00:12:36,150 --> 00:12:42,270 So if you were not successful or we do not have a result, then we can go ahead and set result equal 183 00:12:42,270 --> 00:12:43,980 to this default table. 184 00:12:43,980 --> 00:12:45,150 My bad on that one. 185 00:12:45,150 --> 00:12:48,360 But now we should be good to go to playtest our game. 186 00:12:49,400 --> 00:12:50,030 Perfect. 187 00:12:50,060 --> 00:12:51,740 No errors in the console. 188 00:12:53,140 --> 00:13:00,160 As you can see here I am in my game, and if I go ahead and reset and I die now, I have a death value 189 00:13:00,160 --> 00:13:00,880 of one. 190 00:13:01,520 --> 00:13:06,080 And then if I were to leave my game, it should save the data for my player. 191 00:13:07,350 --> 00:13:10,200 And then if I go back and play test my game again. 192 00:13:11,700 --> 00:13:15,960 Now, as you can see, my deaths have been saved on my player's character. 193 00:13:15,960 --> 00:13:18,390 We can do the same thing for our kills. 194 00:13:18,390 --> 00:13:26,220 If I go to the server and I get to my player and I update their kills value to something like ten. 195 00:13:26,980 --> 00:13:30,280 And then I go back to the client and I were to leave the game. 196 00:13:33,030 --> 00:13:34,170 It should have saved. 197 00:13:34,170 --> 00:13:36,180 And then when I go and play, test it again. 198 00:13:37,270 --> 00:13:43,150 As you can see now I have my value of ten back in my leaderboard as well as my death of one. 199 00:13:43,240 --> 00:13:50,770 Now, let's say we fail to load our data for some reason, and our kills are less than what is stored 200 00:13:50,770 --> 00:13:51,550 in our data store. 201 00:13:51,550 --> 00:13:57,550 So if I go to this server and let's say I update my kills back to zero. 202 00:13:59,380 --> 00:14:05,950 And if I try to leave the game, it shouldn't override my kills value because it's checking if the value 203 00:14:05,950 --> 00:14:13,870 stored in my data store is less than the value stored in the game, so it shouldn't have saved my data. 204 00:14:13,870 --> 00:14:18,280 And if I go back and play the game, as you can see, I still have my ten kills. 205 00:14:18,280 --> 00:14:19,060 Very cool. 206 00:14:20,540 --> 00:14:26,810 So now we have successfully set up our data stores to properly retrieve data when our player joins the 207 00:14:26,810 --> 00:14:27,500 game. 208 00:14:27,620 --> 00:14:33,620 We'll also save data for them when they leave the game, and we also save data intermittently every 209 00:14:33,620 --> 00:14:38,660 five minutes, as well as when the server shuts down and when a player leaves our game. 210 00:14:38,660 --> 00:14:44,630 We also make sure that if they were an Arab player, we clean up any values in our Arab player's table, 211 00:14:44,630 --> 00:14:48,050 so we're not using any memory unnecessarily. 212 00:14:48,290 --> 00:14:52,280 Great work so far on this project and I'll see you in the next lecture.